package com.ssy.lingxi.order.serviceimpl.web;

import com.ssy.lingxi.common.constant.basic.EnableDisableStatus;
import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.common.model.dto.UserLoginCacheDTO;
import com.ssy.lingxi.common.response.PageData;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.order.entity.BaseTradeProcessDO;
import com.ssy.lingxi.order.entity.OrderTradeProcessDO;
import com.ssy.lingxi.order.model.constant.BaseTradeProcessEnum;
import com.ssy.lingxi.order.model.constant.OrderServiceContants;
import com.ssy.lingxi.order.model.vo.basic.request.OrderProcessIdVO;
import com.ssy.lingxi.order.model.vo.basic.request.OrderProcessUpdateStatusVO;
import com.ssy.lingxi.order.model.vo.process.request.OrderProcessNamePageVO;
import com.ssy.lingxi.order.model.vo.process.request.OrderTradeProcessProductPageVO;
import com.ssy.lingxi.order.model.vo.process.request.OrderTradeProcessUpdateVO;
import com.ssy.lingxi.order.model.vo.process.request.OrderTradeProcessVO;
import com.ssy.lingxi.order.model.vo.process.response.BaseTradeProcessVO;
import com.ssy.lingxi.order.model.vo.process.response.OrderProcessPageQueryVO;
import com.ssy.lingxi.order.model.vo.process.response.OrderTradeProcessDetailVO;
import com.ssy.lingxi.order.model.vo.process.response.OrderTradeProcessProductQueryVO;
import com.ssy.lingxi.order.repository.OrderTradeProcessRepository;
import com.ssy.lingxi.order.service.base.*;
import com.ssy.lingxi.order.service.platform.IPlatformTradeProcessService;
import com.ssy.lingxi.order.service.web.IOrderTradeProcessService;
import com.ssy.lingxi.order.utils.NumberUtil;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.persistence.criteria.Predicate;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 交易流程规则配置相关接口实现类
 * @author 万宁
 * @version 2.0.0
 * @date 2021-07-24
 */
@Service
public class OrderTradeProcessServiceImpl implements IOrderTradeProcessService {
    @Resource
    private IBaseCacheService baseCacheService;

    @Resource
    private IBaseTradeProcessService baseTradeProcessService;

    @Resource
    private IPlatformTradeProcessService platformTradeProcessService;

    @Resource
    private IBaseOrderTradeProcessContractService baseOrderTradeProcessContractService;

    @Resource
    private IBaseOrderTradeProcessPaymentService baseOrderTradeProcessPaymentService;

    @Resource
    private IBaseOrderTradeProcessProductService baseOrderTradeProcessProductService;

    @Resource
    private OrderTradeProcessRepository orderTradeProcessRepository;

    /**
     * 分页查询交易流程规则
     *
     * @param headers HttpHeaders信息
     * @param pageVO  接口参数
     * @return 查询结果
     */
    @Override
    public Wrapper<PageData<OrderProcessPageQueryVO>> pageTradeProcesses(HttpHeaders headers, OrderProcessNamePageVO pageVO) {
        UserLoginCacheDTO loginUser = baseCacheService.needLoginFromBusinessPlatform(headers);

        Pageable pageable = PageRequest.of(pageVO.getCurrent() - 1, pageVO.getPageSize(), Sort.by("createTime").descending());
        Specification<OrderTradeProcessDO> specification = (Specification<OrderTradeProcessDO>) (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(criteriaBuilder.equal(root.get("memberId").as(Long.class), loginUser.getMemberId()));
            list.add(criteriaBuilder.equal(root.get("roleId").as(Long.class), loginUser.getMemberRoleId()));

            if(StringUtils.hasLength(pageVO.getName())) {
                list.add(criteriaBuilder.like(root.get("name").as(String.class), "%" + pageVO.getName().trim() + "%"));
            }

            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };

        Page<OrderTradeProcessDO> pageList = orderTradeProcessRepository.findAll(specification, pageable);

        return Wrapper.success(new PageData<>(pageList.getTotalElements(), pageList.getContent().stream().map(process -> {
            OrderProcessPageQueryVO queryVO = new OrderProcessPageQueryVO();
            queryVO.setProcessId(process.getId());
            queryVO.setName(process.getName());
            queryVO.setProcessName(BaseTradeProcessEnum.getNameByCode(process.getBaseTradeProcess().getCode()));
            queryVO.setStatus(process.getStatus());
            queryVO.setStatusName(EnableDisableStatus.getNameByCode(process.getStatus()));
            queryVO.setCreateTime(process.getCreateTime().format(OrderServiceContants.DEFAULT_TIME_FORMATTER));
            return queryVO;
        }).collect(Collectors.toList())));
    }

    /**
     * 新增交易规则页面 - 查询基础交易流程列表
     *
     * @param headers Http头部信息
     * @return 查询结果
     */
    @Override
    public Wrapper<List<BaseTradeProcessVO>> listBaseTradeProcess(HttpHeaders headers) {
        UserLoginCacheDTO loginUser = baseCacheService.needLoginFromBusinessPlatform(headers);
        //从平台后台的配置中查询
        return Wrapper.success(platformTradeProcessService.listOrderTradeProcess(loginUser.getMemberId(), loginUser.getMemberRoleId()));
    }

    /**
     * 新增交易流程规则
     *
     * @param headers   HttpHeaders信息
     * @param processVO 接口参数
     * @return 新增结果
     */
    @Transactional(rollbackFor = BusinessException.class)
    @Override
    public Wrapper<Void> createTradeProcess(HttpHeaders headers, OrderTradeProcessVO processVO) {
        UserLoginCacheDTO loginUser = baseCacheService.needLoginFromBusinessPlatform(headers);

        //Step 1: 判断基础流程
        BaseTradeProcessDO baseTradeProcess = baseTradeProcessService.findById(processVO.getBaseProcessId());
        if(baseTradeProcess == null) {
            return Wrapper.fail(ResponseCode.ORDER_BASE_TRADE_PROCESS_DOES_NOT_EXIST);
        }

        //Step 2: 判断流程类型与商品价格类型的匹配关系
        Wrapper<Void> productResult = baseOrderTradeProcessProductService.checkTradeProcessProducts(baseTradeProcess, processVO.getAllProducts(), processVO.getProducts());
        if(productResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            return Wrapper.fail(productResult.getCode(), productResult.getMessage());
        }

        //Step 3: 查询同类型的流程
        List<OrderTradeProcessDO> tradeProcesses = orderTradeProcessRepository.findByMemberIdAndRoleIdAndShopIdAndProcessType(loginUser.getMemberId(), loginUser.getMemberRoleId(), processVO.getShopId(), baseTradeProcess.getProcessType());
        //如果去掉下面这行代码，则同一个交易流程可配置多次，但仍会根据商品判重
//        if(tradeProcesses.stream().anyMatch(process -> process.getBaseTradeProcess().getId().equals(processVO.getBaseProcessId()))) {
//            return Wrapper.fail(ResponseCode.ORDER_TRADE_PROCESS_EXISTS);
//        }

        //Step 3-1: 判断商品是否在同类型的流程规则配置中
        Wrapper<Void> checkResult = baseOrderTradeProcessProductService.checkProducts(tradeProcesses, processVO.getAllProducts(), processVO.getProducts());
        if(checkResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            return checkResult;
        }

        //Step 3-2: 校验合同参数
        checkResult = baseOrderTradeProcessContractService.checkContract(processVO.getHasContract(), processVO.getContract());
        if(checkResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            return checkResult;
        }

        //Step 3-3：校验支付环节配置
        checkResult = baseOrderTradeProcessPaymentService.checkPayments(baseTradeProcess.getPayTimes(), baseTradeProcess.getPayNodes(), processVO.getPayments());
        if(checkResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            return checkResult;
        }

        //Step 4: 新增
        OrderTradeProcessDO tradeProcess = new OrderTradeProcessDO();
        tradeProcess.setCreateTime(LocalDateTime.now());
        tradeProcess.setMemberId(loginUser.getMemberId());
        tradeProcess.setRoleId(loginUser.getMemberRoleId());
        tradeProcess.setName(processVO.getName());
        tradeProcess.setShopId(processVO.getShopId());
        tradeProcess.setShopType(processVO.getShopType());
        tradeProcess.setShopEnvironment(processVO.getShopEnvironment());
        tradeProcess.setBaseTradeProcess(baseTradeProcess);
        tradeProcess.setProcessType(baseTradeProcess.getProcessType());
        tradeProcess.setPayTimes(baseTradeProcess.getPayTimes());
        tradeProcess.setSkipFirstStep(baseTradeProcess.getSkipFirstStep());
        tradeProcess.setProcessKey(baseTradeProcess.getProcessKey());
        tradeProcess.setProcessKind(baseTradeProcess.getProcessKind());
        tradeProcess.setExpireHours(StringUtils.hasLength(processVO.getExpireHours()) ? new BigDecimal(processVO.getExpireHours()) : BigDecimal.ZERO);
        tradeProcess.setStatus(EnableDisableStatus.ENABLE.getCode());
        tradeProcess.setHasContract(processVO.getHasContract());
        tradeProcess.setAllProducts(processVO.getAllProducts());
        //由于有 CascadeType.DETACH 的关联实体，所以要先保存一下
        orderTradeProcessRepository.saveAndFlush(tradeProcess);

        // 4-1 : 保存合同
        baseOrderTradeProcessContractService.saveContract(tradeProcess, processVO.getHasContract(), processVO.getContract());
        // 4-2 : 保存支付配置
        baseOrderTradeProcessPaymentService.savePayments(tradeProcess, baseTradeProcess.getPayTimes(), baseTradeProcess.getPayNodes(), processVO.getPayments());
        // 4-3 : 保存关联的商品
        baseOrderTradeProcessProductService.saveProducts(tradeProcess, processVO.getAllProducts(), processVO.getProducts());
        //保存
        orderTradeProcessRepository.saveAndFlush(tradeProcess);
        return Wrapper.success();
    }

    /**
     * 查询交易流程规则详情
     *
     * @param headers HttpHeaders信息
     * @param idVO    接口参数
     * @return 查询结果
     */
    @Override
    public Wrapper<OrderTradeProcessDetailVO> getTradeProcess(HttpHeaders headers, OrderProcessIdVO idVO) {
        UserLoginCacheDTO loginUser = baseCacheService.needLoginFromBusinessPlatform(headers);
        OrderTradeProcessDO tradeProcess = orderTradeProcessRepository.findById(idVO.getProcessId()).orElse(null);
        if(tradeProcess == null || !tradeProcess.getMemberId().equals(loginUser.getMemberId())) {
            return Wrapper.fail(ResponseCode.ORDER_TRADE_PROCESS_DOES_NOT_EXIST);
        }

        OrderTradeProcessDetailVO detailVO = new OrderTradeProcessDetailVO();
        detailVO.setProcessId(tradeProcess.getId());
        detailVO.setBaseProcessId(tradeProcess.getBaseTradeProcess().getId());
        detailVO.setProcessType(tradeProcess.getProcessType());
        detailVO.setName(tradeProcess.getName());
        detailVO.setExpireHours(NumberUtil.formatHours(tradeProcess.getExpireHours()));
        detailVO.setShopId(tradeProcess.getShopId());
        detailVO.setShopType(tradeProcess.getShopType());
        detailVO.setShopEnvironment(tradeProcess.getShopEnvironment());
        detailVO.setHasContract(tradeProcess.getHasContract());
        detailVO.setContract(baseOrderTradeProcessContractService.getContract(tradeProcess));
        detailVO.setAllProducts(tradeProcess.getAllProducts());
        detailVO.setPayments(baseOrderTradeProcessPaymentService.findPayments(tradeProcess));
        return Wrapper.success(detailVO);
    }

    /**
     * 分页查询交易流程规则适用商品列表
     *
     * @param headers HttpHeaders信息
     * @param pageVO  接口参数
     * @return 查询结果
     */
    @Override
    public Wrapper<PageData<OrderTradeProcessProductQueryVO>> pageTradeProcessProducts(HttpHeaders headers, OrderTradeProcessProductPageVO pageVO) {
        UserLoginCacheDTO loginUser = baseCacheService.needLoginFromBusinessPlatform(headers);
        OrderTradeProcessDO tradeProcess = orderTradeProcessRepository.findById(pageVO.getProcessId()).orElse(null);
        if(tradeProcess == null || !tradeProcess.getMemberId().equals(loginUser.getMemberId())) {
            return Wrapper.fail(ResponseCode.ORDER_TRADE_PROCESS_DOES_NOT_EXIST);
        }

        return baseOrderTradeProcessProductService.pageProducts(tradeProcess, pageVO.getName(), pageVO.getCurrent(), pageVO.getPageSize());
    }

    /**
     * 修改交易流程规则
     *
     * @param headers  HttpHeaders信息
     * @param updateVO 接口参数
     * @return 修改结果
     */
    @Transactional(rollbackFor = BusinessException.class)
    @Override
    public Wrapper<Void> updateTradeProcess(HttpHeaders headers, OrderTradeProcessUpdateVO updateVO) {
        UserLoginCacheDTO loginUser = baseCacheService.needLoginFromBusinessPlatform(headers);
        OrderTradeProcessDO tradeProcess = orderTradeProcessRepository.findById(updateVO.getProcessId()).orElse(null);
        if(tradeProcess == null || !tradeProcess.getMemberId().equals(loginUser.getMemberId())) {
            return Wrapper.fail(ResponseCode.ORDER_TRADE_PROCESS_DOES_NOT_EXIST);
        }

        //Step 1: 判断基础流程
        BaseTradeProcessDO baseTradeProcess = baseTradeProcessService.findById(updateVO.getBaseProcessId());
        if(baseTradeProcess == null) {
            return Wrapper.fail(ResponseCode.ORDER_BASE_TRADE_PROCESS_DOES_NOT_EXIST);
        }

        //Step 2: 判断流程类型与商品价格类型的匹配关系
        Wrapper<Void> productResult = baseOrderTradeProcessProductService.checkTradeProcessProducts(baseTradeProcess, updateVO.getAllProducts(), updateVO.getProducts());
        if(productResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            return Wrapper.fail(productResult.getCode(), productResult.getMessage());
        }

        //Step 3: 查询同类型的流程
        List<OrderTradeProcessDO> tradeProcesses = orderTradeProcessRepository.findByMemberIdAndRoleIdAndShopIdAndProcessTypeAndIdNot(loginUser.getMemberId(), loginUser.getMemberRoleId(), updateVO.getShopId(), baseTradeProcess.getProcessType(), tradeProcess.getId());
        //如果去掉下面这行代码，则同一个交易流程可配置多次，但仍会根据商品判重
//        if(tradeProcesses.stream().anyMatch(process -> process.getBaseTradeProcess().getId().equals(updateVO.getBaseProcessId()))) {
//            return Wrapper.fail(ResponseCode.ORDER_TRADE_PROCESS_EXISTS);
//        }

        //Step 3-1: 判断商品是否在同类型的流程规则配置中
        Wrapper<Void> checkResult = baseOrderTradeProcessProductService.checkProducts(tradeProcesses, updateVO.getAllProducts(), updateVO.getProducts());
        if(checkResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            return checkResult;
        }

        //Step 3-2: 校验合同参数
        checkResult = baseOrderTradeProcessContractService.checkContract(updateVO.getHasContract(), updateVO.getContract());
        if(checkResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            return checkResult;
        }

        //Step 3-3：校验支付环节配置
        checkResult = baseOrderTradeProcessPaymentService.checkPayments(baseTradeProcess.getPayTimes(), baseTradeProcess.getPayNodes(), updateVO.getPayments());
        if(checkResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            return checkResult;
        }

        //Step 4: 修改
        tradeProcess.setCreateTime(LocalDateTime.now());
        tradeProcess.setMemberId(loginUser.getMemberId());
        tradeProcess.setRoleId(loginUser.getMemberRoleId());
        tradeProcess.setShopId(updateVO.getShopId());
        tradeProcess.setName(updateVO.getName());
        tradeProcess.setShopType(updateVO.getShopType());
        tradeProcess.setBaseTradeProcess(baseTradeProcess);
        tradeProcess.setProcessType(baseTradeProcess.getProcessType());
        tradeProcess.setPayTimes(baseTradeProcess.getPayTimes());
        tradeProcess.setSkipFirstStep(baseTradeProcess.getSkipFirstStep());
        tradeProcess.setProcessKey(baseTradeProcess.getProcessKey());
        tradeProcess.setProcessKind(baseTradeProcess.getProcessKind());
        tradeProcess.setExpireHours(StringUtils.hasLength(updateVO.getExpireHours()) ? new BigDecimal(updateVO.getExpireHours()) : BigDecimal.ZERO);
        tradeProcess.setHasContract(updateVO.getHasContract());
        tradeProcess.setAllProducts(updateVO.getAllProducts());

        // 4-1 : 更新合同
        baseOrderTradeProcessContractService.updateContract(tradeProcess, updateVO.getHasContract(), updateVO.getContract());
        // 4-2 : 更新支付配置
        baseOrderTradeProcessPaymentService.updatePayments(tradeProcess, baseTradeProcess.getPayTimes(), baseTradeProcess.getPayNodes(), updateVO.getPayments());
        // 4-3 : 更新关联的商品
        baseOrderTradeProcessProductService.updateProducts(tradeProcess, updateVO.getAllProducts(), updateVO.getProducts());
        //保存
        orderTradeProcessRepository.saveAndFlush(tradeProcess);
        return Wrapper.success();
    }

    /**
     * 修改交易流程规则状态
     *
     * @param headers  HttpHeaders信息
     * @param statusVO 接口参数
     * @return 修改结果
     */
    @Override
    public Wrapper<Void> updateTradeProcessStatus(HttpHeaders headers, OrderProcessUpdateStatusVO statusVO) {
        UserLoginCacheDTO loginUser = baseCacheService.needLoginFromBusinessPlatform(headers);
        OrderTradeProcessDO tradeProcess = orderTradeProcessRepository.findById(statusVO.getProcessId()).orElse(null);
        if(tradeProcess == null || !tradeProcess.getMemberId().equals(loginUser.getMemberId())) {
            return Wrapper.fail(ResponseCode.ORDER_TRADE_PROCESS_DOES_NOT_EXIST);
        }

        tradeProcess.setStatus(statusVO.getStatus());
        orderTradeProcessRepository.saveAndFlush(tradeProcess);

        return Wrapper.success();
    }

    /**
     * 删除交易流程规则
     *
     * @param headers HttpHeaders信息
     * @param idVO    接口参数
     * @return 删除结果
     */
    @Override
    public Wrapper<Void> deleteTradeProcess(HttpHeaders headers, OrderProcessIdVO idVO) {
        UserLoginCacheDTO loginUser = baseCacheService.needLoginFromBusinessPlatform(headers);
        OrderTradeProcessDO tradeProcess = orderTradeProcessRepository.findById(idVO.getProcessId()).orElse(null);
        if(tradeProcess == null || !tradeProcess.getMemberId().equals(loginUser.getMemberId())) {
            return Wrapper.fail(ResponseCode.ORDER_TRADE_PROCESS_DOES_NOT_EXIST);
        }

        orderTradeProcessRepository.delete(tradeProcess);
        return Wrapper.success();
    }
}
